User blog:Lykrast/A look into the code - Xeadas's Ice Shard
Welcome to this first blog that will tell in details how certains abilities work, in this case . Introduction Don't pay much attention to that on the right for now, it's explained a bit below, and is only so high cause it takes some place. Spells In Warcraft III, spells are made in the object editor, and actually can only be default spells with values tweaked around. This means that the vast majority of abilities in VotE actually CANNOT be done in the object editor alone. But that doesn't mean we won't need it. In our case, needs 3 spells defined in the object editor : *'The main spell (learned and cast by the hero)' : it is based on the Tauren Chieftain's Shockwave, which fires a projectile in a line that does damage to everything in its path. However, due to all the stuff we'll need to do, this one have numbers tweaked so that it doesn't do anything; on its own, casting the spell makes you target a point, then it goes on cd and consume mana, and that's it. *'The slow' : based on the Sorceress's Slow ability, it slows whatever you are targeting for a certain duration, and is in fact used (as a base) for every single ability that slows (with the exception of slowing fields and ). In our case, it has 5 levels, one for each level of the main ability. Unlike the main spell, this one isn't castable by the player, and will be by a dummy unit (explained later). *'The stun' : based on the Mountain King's Stormbolt, it does damage (here, 0) and stuns. Just like the slow it is castable by a dummy unit, and is also used (as a base) for every stun in the map. Triggers Well where the magic happens is in the trigger editor. Here you can put triggers, which are basically : *Event(s) : when something specific happen (ex: a spell is cast), then that trigger is called. *Condition(s) : when the trigger is called, it checks all its conditions in relation to the event that happened (ex: the spell cast is Ice Shard) and, if they are met, execute the action. *Action(s) : when the trigger is called and the conditions are checked, the trigger does that. On the right, you'll see a part of the list of triggers, and in that red rectangle are the 3 that are related to . We'll go in more details right now. The triggers "Ice Shard Cast" Sorry for the stuff in french As you can see here, it triggers on the event "A unit initiates the effect of a spell", which is right at the moment the ability is cast, which is when the mana is spent but before the cooldown actually starts (important for some spells like ). The condition simply requires the spell cast to be our main spell. Then the events, let's go line by line : *Set the variable named "Point" to the position of the "triggering unit" (in our case, whoever cast the spell), this is one of the many variables that I call "temporary", cause no trigger uses them without first giving them a value themself. Why is it done this way is explaining here, but to be simple, it reduces lag. *Set the variable "Point2" to the positon where the player who cast the spell clicked, same reason as above. *Creates an unit called "Ice Shard" at our Point for the owner of the triggering unit, facing towards our Point2. This unit is an invulnerable, untargetable flying unit that can't do anything and looks like the projectile, and is actually the projectile. *Add an expiration timer to our newly created unit, in this case second. This means that this unit will die on its own second after this has been done. This is used to set the range the projectile travels, depending on its speed. *Set our newly created unit's custom value to 1. The custom value is an integer value that can be set for each unit. It doesn't do anything on its own and can only be set via triggers. *And finally, add our newly created unit to a unit group variable, here Xeadas_Ice_Shard_Group. This group contains all active Ice Shards projectile, and it's done this way so that multiple can be active at a time (One For All reasons). *Those two lasts scripts are done to reduce memory leaks, as explained in the link above concerning the first 2 lines. "Ice Shard Death" I want to explain this one before the second one because it is simpler. When an unit dies, and that unit is an Ice Shard, it'll remove it from our Xeadas_Ice_Shard_Group and will clean up the Xeadas_Ice_Shard_Touches group of the corresponding player. Xeadas_Ice_Shard_Touches is another unit group variable, but this time with one for each player (Xeadas_Ice_Shard_Touches1, Xeadas_Ice_Shard_Touches2, ... , Xeadas_Ice_Shard_Touches12). But here it's not directly a number that is written here, so what it does is take the player number (an unique number for each player, ranging from 1 to 12 for real players) of the owner of the triggering unit, here the Ice Shard that just died. Anyway, that "Touches" group contains all unit that the projectile has already hit, so that it only hits each enemy once per cast. "Ice Shard Loop" Here it is, the big one, where all the magic actually happens. Don't worry, I'll try my best to explain. So every second in game, if there is at least one unit in our Xeadas_Ice_Shard_Group, then that long chain will trigger. First, the trigger will pick every unit in our Xeadas_Ice_Shard_Group and will do the actions for each of them, which are as follow : *Set the variable "Unit" (another "temporary" variable) to the unit the "Pick every unit in" loop just picked. *Set the variable "Real" to the damage the spell will do, as follow : **Level of for Heronumber of Owner of Unit (explained above, with triggering unit replace with the variable Unit), HeroX is a variable that refers to the Hero of the player with the player number X. **Multiplied by 50, that gives us , depending on the level of the ability. Because it's in the loop, it does mean leveling the ability mid flight will increase its damage mid flight, but it's not important. **Then we add the APnumber of Owner of Unit variable, multiplied by for our . APX stores the AP of the hero of the player of corresponding player number. *Then, Point is set to the position of Unit. *Point2 is set to Point, but moved 54 units with the angle Unit is facing (which, if you remember from the Cast trigger, is the angle from the initial cast point to the point you targeted). **This means that projectile moves at a speed of 54 units every second, or 1800 units/second. **With the second expiration timer set in the Cast trigger, this means it travels about 720 units (closer to 702 due to the loop nature of this trigger, which can trigger up to 13 times within those second). *Unit is moved instantly to Point2. Well there is no other way to move stuff rather than "instantly", so that "movement" in all dashes and skillshots are actually very short distance teleports made at second intervals, which gives an illusion of movement. *Then Group, which is our "temporary" unit group variable, is set to something so long it's cut in the overview. Basically, it takes Point, gets every unit within 140 units of it, then for each check the conditions. If the conditons are met for a specific unit, it is added to Group. The conditions are as follow : **The matching unit (the one getting tested) is not a building. **The matching unit is alive. **The matching unit doesn't belong to a player who is an ally of the owner of Unit. ***I would have used "belong to a player who is an enemy", but personnal testing show me that "enemy" doesn't seem to includes neutral, so there's that. **The matching unit is not in Xeadas_Ice_Shard_Touchesnumber of owner of Unit. **''Note : this function by default excludes untargetable units, such as another projectile unit.'' *Then for each unit added in our Group variable, it'll do the following things : **Add the newly picked unit (it references the innermost group, that's why the picked unit in the Xeadas_Ice_Shard_Group was stored in a variable) to Xeadas_Ice_Shard_TouchesPnoOoU (short for Player number of owner of Unit, cause it'll be used al lot), so that it won't be picked again until this group is cleared (see above in the Death trigger). **Causes HeroPnoOoU to deal Real damage to the picked unit, Real being set earlier as the damage. **Creates a Dummy unit for the owner of Unit. A dummy unit is an untargetable invulnerable flying unit that is only here to cast a spell. **Creates an expiration timer for our newly created Dummy, so that it'll die. **Give it the slow spell we mentionned at the start of this post. **Set the level of the slow spell the Dummy has to the level of HeroPnOoU's , so that a level 1 will have a 20% slow whereas a level 5 will have a 40% slow. **Order our Dummy to cast "Sorceress - Slow" on the picked unit, this means it'll attempt to cast a spell based on the Slow spell, in our case our slow spell, on our picked unit. **''Note : those 5 lines with the Dummy are really important, since it's how every single slow (except slowing fields and ), every single stun and every single condtional movement speed boost (such as are done.'' **Remember in the Cast trigger where we set the custom value of our projectile to 1 ? Where here, if the custom value of our Unit is 1, then : ***It is set to 0, which means it'll effectively only trigger on the first thing hit by the projectile. ***Just like above, create a Dummy, give it an expiration timer, give it the Stun (0.75) spell (the stun spell from the introduction; I named all non level scaling stuns as "Stun (duration)"), don't set its level since it doesn't scale with levels, then cast it on the picked unit. ***Create a special effect on the target, to indicate that it has been hit by the first target stun, they destroy the visual effect to reduce lag. *Now that all of this is done for every valid target in the projectile's radius, it's time to do this stuff to reduce lag and that's it. Conclusion And there it is, in details. I should add that EVERY skillshot in the map is coded with a code almost identical (some things varies because the spell itself varies), and that this dummy thing is really important. Category:Blog posts